#include "GraphicManager.h"
#include "Utils.h"
#include <cassert>
#include <iostream>
#include <fstream>
#include <sfml/Graphics.hpp>

#include <fbxsdk.h>
#include <fbxfilesdk/kfbxio/kfbximporter.h>
#include <fbxfilesdk/kfbxio/kfbxiosettings.h>
#include <fbxfilesdk/kfbxplugins/kfbxtexture.h>
#include <fbxfilesdk/kfbxplugins/kfbxsdkmanager.h>
#include <fbxfilesdk/kfbxplugins/kfbxscene.h>

#ifdef IOS_REF
#undef  IOS_REF
#define IOS_REF (*(lSdkManager->GetIOSettings()))
#endif

using namespace std;

void GraphicManager::Init()
{
	lSdkManager = KFbxSdkManager::Create();

	if (!lSdkManager)
	{
		printf("Unable to create the FBX SDK manager\n");
		exit(0);
	}

	// create an IOSettings object
	KFbxIOSettings * ios = KFbxIOSettings::Create(lSdkManager, IOSROOT );
	lSdkManager->SetIOSettings(ios);

	// Load plugins from the executable directory
	KString lPath = KFbxGetApplicationDirectory();
#if defined(KARCH_ENV_WIN)
	KString lExtension = "dll";
#elif defined(KARCH_ENV_MACOSX)
	KString lExtension = "dylib";
#elif defined(KARCH_ENV_LINUX)
	KString lExtension = "so";
#endif
	lSdkManager->LoadPluginsDirectory(lPath.Buffer(), lExtension.Buffer());
	
}

void GraphicManager::Destroy()
{
	for (ShaderMap::iterator it = m_shaders.begin(); it!=m_shaders.end(); ++it)
	{
		if(it->second != NULL) glDeleteObjectARB(it->second);
	}

	for (AnimMap::iterator it = m_anims.begin(); it != m_anims.end(); ++it)
	{
		if (it->second.FramesCount != 0 && it->second.Frames != NULL)
		{
			for (int i = 0 ; i < it->second.FramesCount; ++i)
			{
				if (it->second.Frames[i].Skeleton != NULL ) delete [] it->second.Frames[i].Skeleton;
			}
			delete [] it->second.Frames;
		}
	}
	m_anims.clear();

	for (ModelBonesMap::iterator it = m_modelsBones.begin(); it!= m_modelsBones.end(); ++it)
	{
		it->second.clear();
	}
	m_modelsBones.clear();

	for (ModelMap::iterator it = m_models.begin(); it!= m_models.end(); ++it)
	{
		DeleteVBO(it->second.VBOIDs, it->second.TotalMesh);
		if(it->second.Model != NULL)
		{
			it->second.Model->Destroy();
		}

	}
	m_models.clear();

	if (lSdkManager) lSdkManager->Destroy();
	lSdkManager = NULL;

	for (TextureMap::iterator it = m_textures.begin(); it != m_textures.end(); ++it)
	{
		if(it->second != NULL) glDeleteTextures(1, &(it->second));
	}
	m_textures.clear();
}

/*
	GetTexture: search in TextureMap the texture with the given name as key and return it.
*/
GLuint GraphicManager::GetTexture(std::string name)
{
	TextureMap::iterator texture = m_textures.find(name);
	assert( texture!=m_textures.end() && "GraphicManager: GetTexture < Texture not found >" );
	
	return texture->second;
}

/*
	LoadTexture: load the name texture in the Graphics/Textures folder.
	The texture is saved in the TextureMap with given name(without extension) as key.
*/
void GraphicManager::LoadTexture(std::string name)
{
	sf::Image image;
	
	if (!image.LoadFromFile("Graphics/Texture/"+name)) std::cout << "ERROR! Cannot load..";

	glGenTextures(1, &m_texture[0]);
	glBindTexture(GL_TEXTURE_2D, m_texture[0]);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, image.GetWidth(), image.GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, image.GetPixelsPtr());
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

	std::cout << "Inserito:" << name << " come " << name.substr(0,name.length()-4) << " Number: " << m_texture[0] <<std::endl;
	m_textures.insert(std::pair<std::string,GLuint>(name.substr(0,name.length()-4),m_texture[0]));
}

bool GraphicManager::LoadAllTextures()
{
	LoadTexture("aura.png");
	LoadTexture("ball.jpg");
	LoadTexture("body.tga");
	LoadTexture("face.tga");
	LoadTexture("hair.tga");
	LoadTexture("net.png");
	LoadTexture("pitch.png");
	LoadTexture("grass.png");
	LoadTexture("grassout.png");
	LoadTexture("shoes.tga");
	LoadTexture("uniform.jpg");
	LoadTexture("uniform-blue.png");

	return true;
}

/*
	GetModel: search in ModelMap the model with the given name as key, 
	then return a pointer to it.
*/
KFbxScene* GraphicManager::GetModel(std::string name)
{

	ModelMap::iterator model = m_models.find(name);
	assert( model!=m_models.end() && "GraphicManager: GetModel < Model not found >" );

	return model->second.Model;

}

/*
	GetModelData: search in ModelMap the model with the given name as key, 
	then return a pointer to it.
*/
ModelData* GraphicManager::GetModelData(std::string name)
{

	ModelMap::iterator model = m_models.find(name);
	assert( model!=m_models.end() && "GraphicManager: GetModel < Model not found >" );
	
	return &model->second;

}

KFbxScene* GraphicManager::LoadFile(std::string name)
{
	std::string filename = "Graphics/FBX/"+name+".fbx";
	std::cout << std::endl << filename << "..." ;

	KFbxScene* scene = KFbxScene::Create(lSdkManager,"");

	int lFileMajor, lFileMinor, lFileRevision;
	int lSDKMajor,  lSDKMinor,  lSDKRevision;

	bool lStatus;

	// Get the file version number generate by the FBX SDK.
	KFbxSdkManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision);

	// Create an importer.
	KFbxImporter* lImporter = KFbxImporter::Create(lSdkManager,"");

	// Initialize the importer by providing a filename.
	const bool lImportStatus = lImporter->Initialize(filename.c_str(), -1, lSdkManager->GetIOSettings());
	lImporter->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);

	if( !lImportStatus )
	{
		printf("Call to KFbxImporter::Initialize() failed.\n");
		printf("Error returned: %s\n\n", lImporter->GetLastErrorString());

		if (lImporter->GetLastErrorID() == KFbxIO::eFILE_VERSION_NOT_SUPPORTED_YET ||
			lImporter->GetLastErrorID() == KFbxIO::eFILE_VERSION_NOT_SUPPORTED_ANYMORE)
		{
			printf("FBX version number for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision);
			printf("FBX version number for file %s is %d.%d.%d\n\n", filename, lFileMajor, lFileMinor, lFileRevision);
		}

		return NULL;
	}

	printf("FBX version number for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision);

	if (lImporter->IsFBX())
	{
		// Set the import states. By default, the import states are always set to 
		// true. The code below shows how to change these states.
		IOS_REF.SetBoolProp(IMP_FBX_MATERIAL,        true);
		IOS_REF.SetBoolProp(IMP_FBX_TEXTURE,         true);
		IOS_REF.SetBoolProp(IMP_FBX_LINK,            true);
		IOS_REF.SetBoolProp(IMP_FBX_SHAPE,           true);
		IOS_REF.SetBoolProp(IMP_FBX_GOBO,            true);
		IOS_REF.SetBoolProp(IMP_FBX_ANIMATION,       true);
		IOS_REF.SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, true);
	}

	// Import the scene.
	lStatus = lImporter->Import(scene);


	// Destroy the importer.
	lImporter->Destroy();

	if (!lStatus) std::cout << "ERROR! Cannot load..";
	
	return scene;
}

/*
	LoadModel: load the name.3ds model in the Graphics folder.
	The model is saved inth eModelMap with given name as key.
*/
void GraphicManager::LoadModel(std::string name)
{
	
	KFbxScene* scene = LoadFile(name);
	
	if(scene == NULL) return;

	ModelData mData;

	mData.Model = scene;

	//mesh count
	int meshs = 0;
	int totBones = 0;
	BonesMap bonesMap;

	KFbxNode* node = scene->GetRootNode();
	if(node)
	{
		for(int i = 0; i < node->GetChildCount(); i++)
		{
			KFbxNodeAttribute* att = node->GetChild(i)->GetNodeAttribute();

			if( att && att->GetAttributeType() == KFbxNodeAttribute::eMESH) meshs++;
			else
			{
				KFbxXMatrix dummy;
				dummy.SetIdentity();
				CreateBonesMap(bonesMap, node->GetChild(i), dummy, totBones);
			}
		}
	}

	mData.TotalMesh = meshs;
	CreateVBO(mData, bonesMap);

	m_modelsBones.insert(std::pair<std::string, BonesMap>(name, bonesMap));

	bonesMap.clear();

	m_models.insert( std::pair<std::string, ModelData>(name,mData) );
}

void GraphicManager::CreateBonesMap(BonesMap& map, KFbxNode* node, KFbxXMatrix& parentPos, int& totBones)
{
	if(!node) return ;

	KFbxNodeAttribute* att = node->GetNodeAttribute();
	if( !att || att->GetAttributeType() != KFbxNodeAttribute::eSKELETON) return;

	ModelSkeleBone boneData;
	boneData.ID = totBones;

	KFbxXMatrix currentPos = node->GetScene()->GetEvaluator()->GetNodeGlobalTransform(node, 0);

	currentPos *= GetGeometry(node);
		
	boneData.TrasformationMatrix = currentPos;

	currentPos = currentPos.Inverse();

	for(int c = 0; c < 4 ; ++c)
	{
		for (int r = 0; r < 4 ; ++r)
		{	
			boneData.InvertedMatrix[c][r] = static_cast<float>(currentPos.Get(c,r));
		}
	}
	
	/*std::cout << "MATRIX Standard!\n" ;
	std::cout << currentPos.Get(0,0) << " " << currentPos.Get(1,0) << " " << currentPos.Get(2,0) << " " <<  currentPos.Get(3,0) << std::endl; 
	std::cout << currentPos.Get(0,1) << " " << currentPos.Get(1,1) << " " << currentPos.Get(2,1) << " " <<  currentPos.Get(3,1) << std::endl; 
	std::cout << currentPos.Get(0,2) << " " << currentPos.Get(1,2) << " " << currentPos.Get(2,2) << " " <<  currentPos.Get(3,2) << std::endl; 
	std::cout << currentPos.Get(0,3) << " " << currentPos.Get(1,3) << " " << currentPos.Get(2,3) << " " <<  currentPos.Get(3,3) << std::endl; 
	std::cout << "CR -> RC MATRIX\n" ;
	std::cout << boneData.InvertedMatrix[0][0] << " " << boneData.InvertedMatrix[1][0] << " " << boneData.InvertedMatrix[2][0] << " " <<  boneData.InvertedMatrix[3][0] << std::endl; 
	std::cout << boneData.InvertedMatrix[0][1] << " " << boneData.InvertedMatrix[1][1] << " " << boneData.InvertedMatrix[2][1] << " " << boneData.InvertedMatrix[3][1] << std::endl; 
	std::cout << boneData.InvertedMatrix[0][2] << " " << boneData.InvertedMatrix[1][2] << " " << boneData.InvertedMatrix[2][2] << " " << boneData.InvertedMatrix[3][2] << std::endl; 
	std::cout << boneData.InvertedMatrix[0][3] << " " << boneData.InvertedMatrix[1][3] << " " << boneData.InvertedMatrix[2][3] << " " << boneData.InvertedMatrix[3][3] << std::endl; 
	
	KFbxMatrix inv = currentPos.Inverse();
	std::cout << "MATRIX with INVERSE !\n" ;
	std::cout << inv.Get(0,0) << " " << inv.Get(1,0) << " " << inv.Get(2,0) << " " <<  inv.Get(3,0) << std::endl; 
	std::cout << inv.Get(0,1) << " " << inv.Get(1,1) << " " << inv.Get(2,1) << " " <<  inv.Get(3,1) << std::endl; 
	std::cout << inv.Get(0,2) << " " << inv.Get(1,2) << " " << inv.Get(2,2) << " " <<  inv.Get(3,2) << std::endl; 
	std::cout << inv.Get(0,3) << " " << inv.Get(1,3) << " " << inv.Get(2,3) << " " <<  inv.Get(3,3) << std::endl; 
	
	currentPos = currentPos.Transpose();
	std::cout << "MATRIX with Transpose !\n" ;
	std::cout << currentPos.Get(0,0) << " " << currentPos.Get(1,0) << " " << currentPos.Get(2,0) << " " <<  currentPos.Get(3,0) << std::endl; 
	std::cout << currentPos.Get(0,1) << " " << currentPos.Get(1,1) << " " << currentPos.Get(2,1) << " " <<  currentPos.Get(3,1) << std::endl; 
	std::cout << currentPos.Get(0,2) << " " << currentPos.Get(1,2) << " " << currentPos.Get(2,2) << " " <<  currentPos.Get(3,2) << std::endl; 
	std::cout << currentPos.Get(0,3) << " " << currentPos.Get(1,3) << " " << currentPos.Get(2,3) << " " <<  currentPos.Get(3,3) << std::endl; 
	*/

	map.insert(std::pair<KString, ModelSkeleBone>(node->GetName(), boneData));

	totBones++;

	for(int i = 0; i < node->GetChildCount(); i++)
	{
		CreateBonesMap(map, node->GetChild(i), currentPos, totBones);
	}
}

KFbxXMatrix GraphicManager::GetGeometry(KFbxNode* pNode) {
    KFbxVector4 lT, lR, lS;
    KFbxXMatrix lGeometry;

    lT = pNode->GetGeometricTranslation(KFbxNode::eSOURCE_SET);
    lR = pNode->GetGeometricRotation(KFbxNode::eSOURCE_SET);
    lS = pNode->GetGeometricScaling(KFbxNode::eSOURCE_SET);

    lGeometry.SetT(lT);
    lGeometry.SetR(lR);
    lGeometry.SetS(lS);

    return lGeometry;
}

GraphicManager::ModelSkeleBone GraphicManager::GetBoneID(BonesMap& map, KString boneName)
{
	BonesMap::iterator bone = map.find(boneName);
	
	assert( bone!=map.end() && "GraphicManager: GetBoneID < Bone not found >" );

	return bone->second;
}

GraphicManager::BonesMap GraphicManager::GetBoneMap(std::string name)
{
	ModelBonesMap::iterator map = m_modelsBones.find(name);
	
	assert( map != m_modelsBones.end() && "GraphicManager: GetBonesMap < BoneMap not found >" );

	return map->second;
}

void GraphicManager::DeleteVBO(VBOID* VBOIDs, int totMesh)
{
	for(int i=0;i<totMesh;i++)
	{
		if(VBOIDs[i].VerticeID)
			glDeleteBuffersARB(1,&VBOIDs[i].VerticeID);
		if(VBOIDs[i].TexCoordID)
			glDeleteBuffersARB(1,&VBOIDs[i].TexCoordID);
		if(VBOIDs[i].NormalID)
			glDeleteBuffersARB(1,&VBOIDs[i].NormalID);
	}
	delete[] VBOIDs;
	VBOIDs=NULL;
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
}

void GraphicManager::CreateVBO(ModelData& mData, BonesMap& bonesMap)
{
	meshLoaded = 0;

	mData.VBOIDs = new VBOID[mData.TotalMesh];

	for(int i=0;i<mData.TotalMesh;i++)
	{
		mData.VBOIDs[i].VerticeID=0;
		mData.VBOIDs[i].TexCoordID=0;
		mData.VBOIDs[i].NormalID=0;
		mData.VBOIDs[i].TextureID=0;
		mData.VBOIDs[i].TwoSides=false;
	}

	KFbxNode* node = mData.Model->GetRootNode();
	if(node)
	{
		for(int i = 0; i < node->GetChildCount(); i++)
		{
			LoadNode(mData, bonesMap, node->GetChild(i));
		}
	}
}

void GraphicManager::LoadNode(ModelData& mData, BonesMap& bonesMap, KFbxNode* node)
{
	if(!node) return ;

	KFbxNodeAttribute* att = node->GetNodeAttribute();
	if( !att || att->GetAttributeType() != KFbxNodeAttribute::eMESH) return;
	
	KFbxMesh* mesh = static_cast<KFbxMesh*>(node->GetNodeAttribute());
	cout << "Mesh Name: " << node->GetName() << endl;
	
	if (!mesh)	return;

	//check if mesh has vertex
	if(mesh->GetControlPointsCount() <= 0) return;

	cout << "Loading mesh: " << meshLoaded << endl; 

	if(meshLoaded >= mData.TotalMesh) return;

	// Create a copy of the vertex array to receive vertex deformations.
	int vCount = mesh->GetControlPointsCount();
	KFbxVector4* vertex = new KFbxVector4[vCount];
	memcpy(vertex, mesh->GetControlPoints(), vCount * sizeof(KFbxVector4));

	KFbxXMatrix& mm = node->GetScene()->GetEvaluator()->GetNodeGlobalTransform(node);
	KFbxMatrix pos = mm;

	for (int i = 0; i < vCount; ++i)
	{
		vertex[i] = pos.MultNormalize(vertex[i]);
	}

	VBOID* VBOIDs = mData.VBOIDs; // just for easy call 

	//making and clearing space to store vertex
	int vertexCount = mesh->GetPolygonCount()*3*4;
	Vertex * VBOverticesBuffer = new Vertex[vertexCount];
	for (int i = 0; i < vertexCount; ++i)
	{
		VBOverticesBuffer[i].Bone= 0;
		VBOverticesBuffer[i].BoneWeight=0.f;
	}

	float * VBONormalsBuffer = new float[mesh->GetPolygonCount()*3*3*4];

	float * VBOTexCoordBuffer = NULL;

	KFbxLayerElement::EMappingMode mapMode = KFbxLayerElement::eNONE;
	if(mesh->GetLayer(0) && mesh->GetLayer(0)->GetUVs())
		mapMode = mesh->GetLayer(0)->GetUVs()->GetMappingMode();

	KFbxLayerElementArrayTemplate<KFbxVector2> *TexCoords;
	bool IsTexCoords = mesh->GetTextureUV(&TexCoords);

	float tex_scale[] = {0.f,0.f};

	if (node->GetMaterialCount() > 0)
	{
		KFbxSurfaceMaterial* mat = node->GetMaterial( 0 );
		KFbxProperty props = NULL;
		props = mat->FindProperty(KFbxSurfaceMaterial::sDiffuse);

		if(props.IsValid())
		{		
			KFbxTexture* texture = KFbxCast <KFbxTexture>(props.GetSrcObject(KFbxTexture::ClassId, 0)); 

			if (texture != NULL)
			{
				tex_scale[0] = static_cast<float>(texture->GetScaleU());
				tex_scale[1] = static_cast<float>(texture->GetScaleV());
				VBOIDs[meshLoaded].TextureID = GraphicManager::Istance()->GetTexture(texture->GetName());
			}
			//if(mat->two_sided) VBOIDs[node->user_id].TwoSides=true;
		}
	}
	if(IsTexCoords) VBOTexCoordBuffer=new float[mesh->GetPolygonCount()*3*2*4];

	int TotalVertx = 0;
	VBOIDs[meshLoaded].TrianglesArray = true;

	cout << "TEXTURE ID: " <<VBOIDs[meshLoaded].TextureID<<endl;

	int defCount = mesh->GetDeformerCount(KFbxDeformer::eSKIN);
	KFbxSkin* deformer = NULL;
	
	if(defCount == 1) deformer =  static_cast<KFbxSkin*>(mesh->GetDeformer(0, KFbxDeformer::eSKIN));
	
	for (int ipoly = 0; ipoly < mesh->GetPolygonCount(); ++ipoly)
	{
		if(VBOIDs[meshLoaded].TrianglesArray && mesh->GetPolygonSize(ipoly)!=3) VBOIDs[meshLoaded].TrianglesArray=false;

		for (int ivert = 0; ivert < mesh->GetPolygonSize(ipoly); ++ivert)
		{

			//NORMALS
			KFbxVector4 normal;
			mesh->GetPolygonVertexNormal(ipoly,ivert,normal);
			VBONormalsBuffer[TotalVertx*3+ivert*3+0] = static_cast<float>(normal[0]);
			VBONormalsBuffer[TotalVertx*3+ivert*3+1] = static_cast<float>(normal[1]);
			VBONormalsBuffer[TotalVertx*3+ivert*3+2] = static_cast<float>(normal[2]);

			if (IsTexCoords)
			{
				int lCurrentUVIndex;

				if (mapMode == KFbxLayerElement::eBY_POLYGON_VERTEX)
				{
					lCurrentUVIndex = mesh->GetTextureUVIndex(ipoly, ivert);
				}
				else // KFbxLayerElement::eBY_CONTROL_POINT
				{
					lCurrentUVIndex = mesh->GetPolygonVertex(ipoly, ivert);
				}

				VBOTexCoordBuffer[TotalVertx*2+ivert*2+0] = tex_scale[0] * static_cast<float>(TexCoords->GetAt(lCurrentUVIndex).mData[0]);
				VBOTexCoordBuffer[TotalVertx*2+ivert*2+1] = tex_scale[1] - static_cast<float>(TexCoords->GetAt(lCurrentUVIndex).mData[1]) * tex_scale[1];
			}
			
			int vertexNumber = mesh->GetPolygonVertex(ipoly, ivert);
			
			//If a Skeleton exists, find 4 most important bones for this vertex
			if (deformer != NULL)
			{
				KString boneName;
				int bonesFounded = false;
				const int numCluster = deformer->GetClusterCount();
				for (int c = 0; c < numCluster; ++c)
				{
					KFbxCluster* cluster = deformer->GetCluster(c);
					//cout << "CLUSTER: "<< cluster->GetControlPointIndicesCount() << endl;
					int totalPI = cluster->GetControlPointIndicesCount();
					int* index = cluster->GetControlPointIndices();
					double* weights = cluster->GetControlPointWeights();
					
					for (int i = 0; i < totalPI; ++i)
					{
						if (index[i]==vertexNumber)
						{
							if(!bonesFounded) //first dependecy found.
							{
								VBOverticesBuffer[TotalVertx+ivert].BoneWeight = static_cast<float>(weights[i]);
								KFbxNode* cNode = cluster->GetLink();
								boneName = cNode->GetName();
								VBOverticesBuffer[TotalVertx+ivert].Bone = GetBoneID(bonesMap, boneName).ID;
								bonesFounded = true;
							}
							//check if the new bone is better than the old worst
							else if (VBOverticesBuffer[TotalVertx+ivert].BoneWeight < static_cast<float>(weights[i]))
							{
								VBOverticesBuffer[TotalVertx+ivert].BoneWeight = static_cast<float>(weights[i]);
								KFbxNode* cNode = cluster->GetLink();
								boneName = cNode->GetName();
								VBOverticesBuffer[TotalVertx+ivert].Bone = GetBoneID(bonesMap, boneName).ID;
							}
						}
					}
				}
				float vertPos[4] = {0.f,0.f,0.f,1.0f};
				Matrix& invBone =  GetBoneID(bonesMap, boneName).InvertedMatrix;

				for (int i = 0; i < 4; ++i)
				{
					for (int j = 0; j < 4; ++j)
					{
						vertPos[i] += invBone[j][i] * static_cast<float>(vertex[vertexNumber][j]);
					}
				}

				VBOverticesBuffer[TotalVertx+ivert].Coords[0] = vertPos[0];
				VBOverticesBuffer[TotalVertx+ivert].Coords[1] = vertPos[1];
				VBOverticesBuffer[TotalVertx+ivert].Coords[2] = vertPos[2];

			}
			else
			{
				VBOverticesBuffer[TotalVertx+ivert].Coords[0] = static_cast<float>(vertex[vertexNumber][0]);
				VBOverticesBuffer[TotalVertx+ivert].Coords[1] = static_cast<float>(vertex[vertexNumber][1]);
				VBOverticesBuffer[TotalVertx+ivert].Coords[2] = static_cast<float>(vertex[vertexNumber][2]);
			}

			
		}

		TotalVertx +=  mesh->GetPolygonSize(ipoly);

	}

	VBOIDs[meshLoaded].VerticeNum = TotalVertx;

	glGenBuffersARB( 1,&VBOIDs[meshLoaded].VerticeID);
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOIDs[meshLoaded].VerticeID );
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, TotalVertx*sizeof(Vertex), VBOverticesBuffer, GL_STATIC_DRAW_ARB );
	delete[] VBOverticesBuffer;
	VBOverticesBuffer=NULL;

	glGenBuffersARB( 1,&VBOIDs[meshLoaded].NormalID);
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOIDs[meshLoaded].NormalID );
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, TotalVertx*3*sizeof(float), VBONormalsBuffer, GL_STATIC_DRAW_ARB );
	delete[] VBONormalsBuffer;
	VBONormalsBuffer=NULL;

	if(IsTexCoords)
	{
		glGenBuffersARB( 1,&VBOIDs[meshLoaded].TexCoordID);
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOIDs[meshLoaded].TexCoordID );
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, TotalVertx*2*sizeof(float), VBOTexCoordBuffer, GL_STATIC_DRAW_ARB );
		delete[] VBOTexCoordBuffer;
		VBOTexCoordBuffer=NULL;
	}

	delete [] vertex;

	meshLoaded++;
}

bool GraphicManager::LoadAllModels()
{
	LoadModel("Ball");
	LoadModel("Player");
	LoadModel("Stadium");

	return true;
}

void GraphicManager::LoadAnim(std::string name)
{
	KFbxScene* scene = LoadFile(name);

	if(scene == NULL) return;

	Anim anim;
	anim.BonesCount = 0;
	anim.FramesCount = 0;
	anim.Frames = NULL;

	KFbxAnimStack* kfbx_anim;
	KTime stop;
	KTime period;

	for (int i = 0; i < scene->GetSrcObjectCount(FBX_TYPE(KFbxAnimStack)); i++) //it's always just one anim for file.
	{
		kfbx_anim = KFbxCast<KFbxAnimStack>(scene->GetSrcObject(FBX_TYPE(KFbxAnimStack), i));
		KTimeSpan tp;
		scene->GetGlobalSettings().GetTimelineDefaultTimeSpan(tp);
		stop = tp.GetStop();
		period.SetTime(0,0,0,1,0,scene->GetGlobalSettings().GetTimeMode());
	}

	//Creating Frames
	KTime start = 0;
	int fcount = 0;
	while ( (start = start + period) <= stop )
	{
		fcount++;
	}
	cout << "FRAMES COUNT :" << fcount << endl;

	anim.FramesCount = fcount;
	anim.Frames = new Frame[fcount];
	for (int i = 0; i < fcount; ++i)
	{
		anim.Frames[i].Skeleton= NULL;
	}

	//Creating Bones
	KFbxNode* node = scene->GetRootNode();
	int totBones = 0;
	for(int i = 0; i < node->GetChildCount(); i++)
	{
		CountBones(node->GetChild(i), totBones);
	}
	cout << "BONES COUNT :" << totBones << endl;
	
	anim.BonesCount = totBones;
	
	for (int i = 0; i < fcount; ++i)
	{
		anim.Frames[i].Skeleton = new Bone[totBones];
	}

	start = 0;

	//Our Anim container is now ready to store datas
	for (int iframe = 0; iframe < fcount; ++iframe)
	{
		KFbxNode* node = scene->GetRootNode();
		int boneCounter = 0;
		for(int i = 0; i < node->GetChildCount(); i++)
		{
			KFbxXMatrix dummyPos;
			SaveSkinData(anim, node->GetChild(i), boneCounter, dummyPos, start, iframe, boneCounter);
		}
		start += period;
	}

	m_anims.insert(std::pair<std::string,Anim>(name,anim));

	scene->Destroy();
}

void GraphicManager::CountBones(KFbxNode* node, int& totBones)
{
	if(!node) return ;

	KFbxNodeAttribute* att = node->GetNodeAttribute();
	if( !att || att->GetAttributeType() != KFbxNodeAttribute::eSKELETON) return;

	totBones++;
	
	for(int i = 0; i < node->GetChildCount(); i++)
	{
		CountBones(node->GetChild(i), totBones);
	}
}

void GraphicManager::SaveSkinData(Anim& anim, KFbxNode* node, int parentID, KFbxXMatrix& parentPos, const KTime& ktFrame, int currentFrame, int& currentBones)
{
	if(!node) return ;

	KFbxNodeAttribute* att = node->GetNodeAttribute();
	if( !att || att->GetAttributeType() != KFbxNodeAttribute::eSKELETON) return;
	
	KFbxSkeleton* skele = (KFbxSkeleton*) node->GetNodeAttribute(); 

	KFbxXMatrix currentPos =  node->GetScene()->GetEvaluator()->GetNodeGlobalTransform(node, ktFrame);

	currentPos *= GetGeometry(node);

	for(int c = 0; c < 4 ; ++c)
	{
		for (int r = 0; r < 4 ; ++r)
		{	
			anim.Frames[currentFrame].Skeleton[currentBones].GlobalPosition[c][r] = static_cast<float>(currentPos.Get(r,c));
		}
	}

	anim.Frames[currentFrame].Skeleton[currentBones].ParentBone = parentID;
	int myID = currentBones;
	currentBones++;

	for(int i = 0; i < node->GetChildCount(); i++)
	{
		SaveSkinData(anim, node->GetChild(i), myID, currentPos, ktFrame, currentFrame, currentBones);
	}
}

Anim& GraphicManager::GetAnim(std::string name)
{
	AnimMap::iterator anim = m_anims.find(name);
	assert( anim!=m_anims.end() && "GraphicManager: GetAnim < Anim not found >" );

	return anim->second;
}

bool GraphicManager::LoadAllAnims()
{
	//LoadAnim("PlayerStand");
	LoadAnim("PlayerWalk");
	//LoadAnim("PlayerRun");

	return true;
}

GLuint GraphicManager::GetShader(std::string name)
{
	ShaderMap::iterator shader = m_shaders.find(name);
	assert( shader!=m_shaders.end() && "GraphicManager: GetShader < Shader not found >" );

	return shader->second;
	
}

void GraphicManager::LoadShader(std::string name, GLuint& vertID, GLuint& fragID)
{
	char *vs,*fs;
	vertID = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	fragID = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
	vs = ReadTextFile( std::string(name+".vert").c_str() );
	fs = ReadTextFile( std::string(name+".frag").c_str() );

	const char * vv = vs;
	const char * ff = fs;

	glShaderSourceARB(vertID, 1, &vv,NULL);
	glShaderSourceARB(fragID, 1, &ff,NULL);

	free(vs);free(fs);

	glCompileShaderARB(vertID);
	glCompileShaderARB(fragID);
}

bool GraphicManager::LoadAllShaders()
{
	GLuint v, f, p;

	LoadShader("base",v,f);
	
	p = glCreateProgramObjectARB();

	glAttachObjectARB(p,v);
	glAttachObjectARB(p,f);

	glBindAttribLocation(p, 0, "vertex");
	glBindAttribLocation(p, 2, "normal");
	glBindAttribLocation(p, 8, "texCoord");
	glLinkProgramARB(p);

	m_shaders.insert(std::pair<std::string, GLuint>("base", p));


	v = 0;
	f = 0;
	p = 0;
	LoadShader("baseTextured",v,f);

	p = glCreateProgramObjectARB();

	glAttachObjectARB(p,v);
	glAttachObjectARB(p,f);

	glBindAttribLocation(p, 0, "vertex");
	glBindAttribLocation(p, 2, "normal");
	glBindAttribLocation(p, 8, "texCoord");
	glLinkProgramARB(p);

	m_shaders.insert(std::pair<std::string, GLuint>("baseTextured", p));

	v = 0;
	f = 0;
	p = 0;
	LoadShader("skin",v,f);

	p = glCreateProgramObjectARB();

	glAttachObjectARB(p,v);
	glAttachObjectARB(p,f);

	glBindAttribLocation(p, 0, "vertex");
	glBindAttribLocation(p, 2, "normal");
	glBindAttribLocation(p, 8, "texCoord");
	glBindAttribLocation(p, 6, "bones");
	glBindAttribLocation(p, 7, "weight");
	glLinkProgramARB(p);

	m_shaders.insert(std::pair<std::string, GLuint>("skin", p));

	return true;
}

char* GraphicManager::ReadTextFile(const char *fn) 
{
	FILE *fp;
	char *content = NULL;

	int count=0;

	if (fn != NULL) {
		fp = fopen(fn,"rt");

		if (fp != NULL) {

			fseek(fp, 0, SEEK_END);
			count = ftell(fp);
			rewind(fp);

			if (count > 0) {
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content,sizeof(char),count,fp);
				content[count] = '\0';
			}
			fclose(fp);
		}
	}
	else {
		cout<< "Error reading\n";
	}
	return content;
}